package com.cyz.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cyz.exception.BusinessException;
import com.cyz.exception.ErrorCode;
import com.cyz.exception.ThrowUtils;
import com.cyz.mapper.SpaceMapper;
import com.cyz.model.dto.space.analyze.*;
import com.cyz.model.entity.Picture;
import com.cyz.model.entity.Space;
import com.cyz.model.entity.User;
import com.cyz.model.vo.space.analyze.*;
import com.cyz.service.PictureService;
import com.cyz.service.SpaceAnalyzeService;
import com.cyz.service.SpaceService;
import com.cyz.service.UserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SpaceAnalyzeServiceImpl extends ServiceImpl<SpaceMapper, Space>
        implements SpaceAnalyzeService {

    @Resource
    private SpaceService spaceService;

    @Resource
    private UserService userService;

    @Resource
    private PictureService pictureService;


    /**
     * 获取空间使用分析数据
     * @param spaceAnalyzeRequest 请求参数
     * @param loginUser           操作用户
     * @return  分析结果
     */
    @Override
    public SpaceUsageAnalyzeResponse getSpaceUsageAnalyze(SpaceAnalyzeRequest spaceAnalyzeRequest, User loginUser) {
        // 获取参数
        Long spaceId = spaceAnalyzeRequest.getSpaceId();
        boolean queryPublic = spaceAnalyzeRequest.isQueryPublic();
        boolean queryAll = spaceAnalyzeRequest.isQueryAll();
        // 校验权限
        checkSpaceAnalyzeAuth(spaceAnalyzeRequest, loginUser);
        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        SpaceUsageAnalyzeResponse spaceUsageAnalyzeResponse = new SpaceUsageAnalyzeResponse();
        // 判断是分析 公共空间图片、所有图片还是 分析私有空间
        if (queryPublic || queryAll) {
            // 分析的是公共空间图片或所有图片，
            // 补充查询条件
            queryWrapper.select("picSize");
            fillAnalyzeQueryWrapper(spaceAnalyzeRequest, queryWrapper);
            // 根据查询条件获取图片信息
            List<Object> objects = pictureService.listObjs(queryWrapper);
            // 解析数据
            long usedSize = objects.stream().mapToLong(object -> (long) object).sum();
            long usedCount = objects.size();
            // 封装返回结果
            spaceUsageAnalyzeResponse.setUsedSize(usedSize);
            spaceUsageAnalyzeResponse.setUsedCount(usedCount);
            // 公共图库没有存储上限
            spaceUsageAnalyzeResponse.setMaxSize(null);
            spaceUsageAnalyzeResponse.setSizeUsageRatio(null);
            spaceUsageAnalyzeResponse.setMaxCount(null);
            spaceUsageAnalyzeResponse.setCountUsageRatio(null);
            return spaceUsageAnalyzeResponse;
        } else {
            // 分析私有空间信息
            // 根据spaceId获取空间信息
            Space space = spaceService.getById(spaceId);
            // 封装返回结果
            spaceUsageAnalyzeResponse.setUsedSize(space.getTotalSize());
            spaceUsageAnalyzeResponse.setUsedCount(space.getTotalCount());
            spaceUsageAnalyzeResponse.setMaxSize(space.getMaxSize());
            spaceUsageAnalyzeResponse.setMaxCount(space.getMaxCount());
            // 计算比例
            double usedSizeRatio = NumberUtil.round(space.getTotalSize() * 100.0 / space.getMaxSize(), 2).doubleValue();
            double usedCountRatio = NumberUtil.round(space.getTotalCount() * 100.0 / space.getMaxCount(), 2).doubleValue();
            spaceUsageAnalyzeResponse.setSizeUsageRatio(usedSizeRatio);
            spaceUsageAnalyzeResponse.setCountUsageRatio(usedCountRatio);

            return spaceUsageAnalyzeResponse;
        }

    }

    /**
     * 按照分类来分析数据
     *
     * @param spaceCategoryAnalyzeRequest   请求参数
     * @param loginUser 操作用户
     * @return  分析结果
     */
    @Override
    public List<SpaceCategoryAnalyzeResponse> getSpaceCategoryAnalyze(SpaceCategoryAnalyzeRequest spaceCategoryAnalyzeRequest, User loginUser) {

        // 校验权限
        checkSpaceAnalyzeAuth(spaceCategoryAnalyzeRequest, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        fillAnalyzeQueryWrapper(spaceCategoryAnalyzeRequest, queryWrapper);

        // 查询图片分类信息
        queryWrapper.select("category", "count(*) as count", "sum(picSize) as totalSize")
                .groupBy("category");

        // 转换并返回结果
        return pictureService.getBaseMapper().selectMaps(queryWrapper).stream().map(result -> {
            String category = (result.get("category") != null && result.get("category") != "") ? result.get("category").toString() : "未分类";
            Long count = (Long) result.get("count");
            Long totalSize = ((Number) result.get("totalSize")).longValue();
            return new SpaceCategoryAnalyzeResponse(category, count, totalSize);
        }).collect(Collectors.toList());
    }

    /**
     * 按照标签分析数据
     *
     * @param spaceTagAnalyzeRequest    请求参数
     * @param loginUser 操作用户
     * @return  分析结果
     */
    @Override
    public List<SpaceTagAnalyzeResponse> getSpaceTagAnalyze(SpaceTagAnalyzeRequest spaceTagAnalyzeRequest, User loginUser) {

        // 校验权限
        checkSpaceAnalyzeAuth(spaceTagAnalyzeRequest, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        fillAnalyzeQueryWrapper(spaceTagAnalyzeRequest, queryWrapper);

        // 查询图片标签信息
        queryWrapper.select("tags");
        List<Object> tagObjList = pictureService.listObjs(queryWrapper);

        // 转换查询结果
        List<String> tagJsonList = tagObjList.stream()
                .filter(ObjUtil::isNotNull)
                .map(Object::toString)
                .collect(Collectors.toList());

        // 分析标签数据，先将标签数据扁平化
        // {'["java", "python", "c++"]', '["java", "python", "c++"]'} => {"java", "python", "c++", "java", "python", "c++"}
        Map<String, Long> tagCountList = tagJsonList.stream()
                .flatMap(tagJson -> JSONUtil.toList(tagJson, String.class).stream())
                .collect(Collectors.groupingBy(tag -> tag, Collectors.counting()));

        // 排序并返回结果
        return tagCountList.entrySet().stream()
                .sorted((e1, e2) -> Long.compare(e1.getValue(), e2.getValue()))
                .map(entry -> new SpaceTagAnalyzeResponse(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());
    }

    /**
     * 按照图片大小分析
     *
     * @param spaceSizeAnalyzeRequest   请求参数
     * @param loginUser 操作用户
     * @return  分析结果
     */
    @Override
    public List<SpaceSizeAnalyzeResponse> getSpaceSizeAnalyze(SpaceSizeAnalyzeRequest spaceSizeAnalyzeRequest, User loginUser) {

        // 校验权限
        checkSpaceAnalyzeAuth(spaceSizeAnalyzeRequest, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        fillAnalyzeQueryWrapper(spaceSizeAnalyzeRequest, queryWrapper);

        // 查询图片大小信息
        queryWrapper.select("picSize");
        List<Object> picSizeObjList = pictureService.listObjs(queryWrapper);

        // 转换查询结果
        List<Long> picSizeList = picSizeObjList.stream()
                .filter(ObjUtil::isNotNull)
                .map(picSize -> (Long) picSize)
                .collect(Collectors.toList());

        // 定义图片范围
        LinkedHashMap<String, Long> picSizeRangeMap = new LinkedHashMap<>();
        picSizeRangeMap.put("<100KB", picSizeList.stream().filter(picSize -> picSize < 100 * 1024).count());
        picSizeRangeMap.put("100KB-500KB", picSizeList.stream().filter(picSize -> picSize > 100 * 1024 && picSize < 500 * 1024).count());
        picSizeRangeMap.put("500KB-1MB", picSizeList.stream().filter(picSize -> picSize > 500 * 1024 && picSize < 1024 * 1024).count());
        picSizeRangeMap.put(">1MB", picSizeList.stream().filter(picSize -> picSize > 1024 * 1024).count());

        // 转换成响应结果并返回
        return picSizeRangeMap.entrySet().stream()
                .map(entry -> new SpaceSizeAnalyzeResponse(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());
    }

    /**
     * 根据用户上传日期分析
     *
     * @param spaceUserAnalyzeRequest   请求参数
     * @param loginUser 操作用户
     * @return  分析结果
     */
    @Override
    public List<SpaceUserAnalyzeResponse> getSpaceUserAnalyze(SpaceUserAnalyzeRequest spaceUserAnalyzeRequest, User loginUser) {

        // 校验权限
        checkSpaceAnalyzeAuth(spaceUserAnalyzeRequest, loginUser);

        // 构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        fillAnalyzeQueryWrapper(spaceUserAnalyzeRequest, queryWrapper);
        Long userId = spaceUserAnalyzeRequest.getUserId();
        queryWrapper.eq(userId != null, "userId", userId);

        // 查询图片上传日期信息
        String timeDimension = spaceUserAnalyzeRequest.getTimeDimension();
        switch (timeDimension){
            case "day":
                queryWrapper.select("Date_Format(createTime, '%Y-%m-%d') as timeRange", "count(*) as count");
                break;
            case "week":
                queryWrapper.select("YearWeek(createTime) as timeRange", "count(*) as count");
                break;
            case "month":
                queryWrapper.select("Date_Format(createTime, '%Y-%m') as timeRange", "count(*) as count");
                break;
            default:
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "不支持该时间范围");
        }
        queryWrapper.groupBy("timeRange").orderByAsc("timeRange");

        // 转换并返回结果
        return pictureService.listMaps(queryWrapper).stream()
                .map(result -> new SpaceUserAnalyzeResponse(result.get("timeRange").toString(), (Long)result.get("count")))
                .collect(Collectors.toList());
    }

    /**
     * 根据用户空间使用排名分析（仅管理员）
     * @param spaceRankAnalyzeRequest   请求参数
     * @param loginUser 操作用户
     * @return  分析结果
     */
    @Override
    public List<Space> getSpaceRankAnalyze(SpaceRankAnalyzeRequest spaceRankAnalyzeRequest, User loginUser) {

        // 校验权限
        ThrowUtils.throwIf(!userService.isAdmin(loginUser), ErrorCode.NO_AUTH_ERROR);

        // 构造查询条件
        QueryWrapper<Space> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "spaceName", "userId", "totalSize")
                .orderByDesc("totalSize")
                .last("limit " + spaceRankAnalyzeRequest.getTopN());    // 取前 N 名
        // 返回结果
        return spaceService.list(queryWrapper);
    }

    /**
     * 校验空间分析权限
     *
     * @param spaceAnalyzeRequest
     * @param loginUser
     */
    private void checkSpaceAnalyzeAuth(SpaceAnalyzeRequest spaceAnalyzeRequest, User loginUser) {
        Long spaceId = spaceAnalyzeRequest.getSpaceId();
        boolean queryPublic = spaceAnalyzeRequest.isQueryPublic();
        boolean queryAll = spaceAnalyzeRequest.isQueryAll();

        // 校验权限
        if (queryPublic || queryAll) {
            // 分析所有公共空间或所有空间的图片，只有管理员有权限
            ThrowUtils.throwIf(!userService.isAdmin(loginUser), ErrorCode.NO_AUTH_ERROR);
        } else {
            // 分析私有空间，只有空间所有者和管理员能够分析
            ThrowUtils.throwIf(spaceId == null || spaceId < 0, ErrorCode.PARAMS_ERROR);
            Space space = spaceService.getById(spaceId);
            ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "该空间不存在");
            ThrowUtils.throwIf(
                    !space.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser),
                    ErrorCode.NO_AUTH_ERROR);
        }
    }

    /**
     * 根据分析范围填充查询参数
     *
     * @param spaceAnalyzeRequest
     * @param queryWrapper
     */
    private static void fillAnalyzeQueryWrapper(SpaceAnalyzeRequest spaceAnalyzeRequest, QueryWrapper<Picture> queryWrapper) {
        if (spaceAnalyzeRequest.isQueryAll()) {
            // 分析所有图片（包括私有空间图片）
            return;
        }
        if (spaceAnalyzeRequest.isQueryPublic()) {
            // 分析公共空间的图片
            queryWrapper.isNull("spaceId");
            return;
        }
        Long spaceId = spaceAnalyzeRequest.getSpaceId();
        if (spaceId != null) {
            // 分析私有空间的图片
            queryWrapper.eq("spaceId", spaceId);
            return;
        }
        throw new BusinessException(ErrorCode.PARAMS_ERROR, "未指定查询范围");
    }

}
